<template>
  <div class="box">
    <!--自定义拓朴结构-->
    <el-dialog title="自定义拓扑结构" :visible.sync="dialogVisible" width="80%">
      <div style="display: flex;">
        <div style="width: 30%">
          <div style="display: flex;flex-flow: row nowrap; justify-content: space-between;margin-bottom: 10px;">
            <el-alert title="最多支持三层拓扑结构" type="warning" center show-icon :closable="false"></el-alert>
            <el-button style="margin-left: 20px;" type="primary" @click="showSelfDefineTopology">查看拓扑结构</el-button>
          </div>
          <el-tree
              :data="treeData"
              node-key="name"
              default-expand-all>
              <span class="custom-tree-node" slot-scope="{ node, data }">
                <span>{{ node.data.name }}</span>
                <span>
                  <el-dropdown @command="handleAddCommand">
                    <span v-if="data.level < 3 && data.type !== 'node'" style="color:#409EFF; margin-right:20px;" class="el-dropdown-link">
                      新增<i class="el-icon-arrow-down el-icon--right"></i>
                    </span>
                    <el-dropdown-menu slot="dropdown">
                      <el-dropdown-item :disabled="data.type==='node' || data.type==='hub'"
                                        :command="{type: 'bridge', node: node}">Bridge</el-dropdown-item>
                      <el-dropdown-item :disabled="data.type==='node' || data.type==='hub'"
                                        :command="{type: 'hub', node: node}">Hub</el-dropdown-item>
                      <el-dropdown-item :command="{type: 'node', node: node}">node</el-dropdown-item>
                    </el-dropdown-menu>
                  </el-dropdown>
                  <el-button v-show="data.level !== 1"
                      type="text"
                      size="mini"
                      style="color: #F56C6C"
                      @click="() => treeRemove(node, data)">
                    删除
                  </el-button>
                </span>
              </span>
            </el-tree>
          </div>
          <div style="width: 70%; min-height: 500px">
            <div id="selfTopologyChart" style="height: 100%; width: 100%; background-color: rgb(233, 233, 235)"></div>
          </div>
        </div>
        <span slot="footer" class="dialog-footer">
          <el-button @click="dialogVisible = false">取 消</el-button>
          <el-button type="primary" @click="handleSelfDefineConfirm">确 定</el-button>
        </span>
    </el-dialog>
    <div class="main-box" :style="{width: 0.6*windowWidth+'px', height: windowHeight+'px'}">
      <h3>工具栏</h3>
      <div class="form-box">
        <el-form ref="form" style="width: 90%" :inline="true" :model="toolForm" label-position="right">
          <el-row>
            <el-col :span="8">
              <el-form-item label="消息提示:">
                <el-switch v-model="toolForm.showSendText"></el-switch>
              </el-form-item>
            </el-col>
            <el-col :span="8">
              <el-form-item label="自动随机发送:">
                <el-switch v-model="toolForm.autoSend" @change="handleAutoSendChange"></el-switch>
              </el-form-item>
            </el-col>
            <el-col :span="8">
              <el-form-item label="随机帧个数:">
                <el-input-number v-model="randomCount" :disabled="!toolForm.autoSend" size="small" :min="0"
                                 :max="4"></el-input-number>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-col :span="6">
              <el-form-item label="手动模拟:">
                <el-switch v-model="toolForm.handSend" @change="handleHandSendChange"></el-switch>
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="帧个数:">
                <el-input-number v-model="randomCount" :disabled="!toolForm.handSend" size="small" :min="0"
                                 :max="4"></el-input-number>
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="下一步:">
                <el-button type="primary" size="small" :disabled="!toolForm.handSend"
                           @click="handGenerateLines"
                           :loading="toolForm.handWaiting">下一步
                </el-button>
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="自定义:">
                <el-button type="warning" size="small" @click="handleSelfDefine" :disabled="!toolForm.handSend">
                  自定义拓扑结构
                </el-button>
              </el-form-item>
            </el-col>
          </el-row>

        </el-form>
      </div>
      <div id="topologyChart" style="height: 80%; width: 100%"></div>
    </div>
    <div class="info-box" :style="{width: 0.4*windowWidth+'px', height: windowHeight+'px'}">
      <h3>运行信息</h3>
      <el-descriptions :column="3" border>
        <el-descriptions-item label="节点个数">{{ this.nodeCount }}</el-descriptions-item>
        <el-descriptions-item label="网桥个数">{{ this.bridgeCount }}</el-descriptions-item>
        <el-descriptions-item label="集线器个数">{{ this.hubCount }}</el-descriptions-item>
        <el-descriptions-item label="发送MAC帧总数">
          <span class="p-wrapper">{{ this.msgSendCount }}</span>
        </el-descriptions-item>
        <el-descriptions-item label="接受MAC帧总数">
          <span class="p-wrapper">{{ this.msgReceiveCount }}</span>
        </el-descriptions-item>
        <el-descriptions-item label="成功率">
          <span class="p-wrapper">{{ successRate }}</span>
        </el-descriptions-item>
        <el-descriptions-item label="启动时长">
          <TimeCountDown></TimeCountDown>
        </el-descriptions-item>
        <el-descriptions-item label="统计周期">{{ this.transmissionTime }}s/次</el-descriptions-item>
        <el-descriptions-item label="MAC帧传输模拟用时">{{ this.transmissionTime }}s</el-descriptions-item>
      </el-descriptions>
      <h3>网桥转发表</h3>
      <div style="display: flex;">
        <div class="table-box" v-for="(table, index) in forwardTables" :key="table.name">
          <el-button style="float:left" size="small" plain type="primary" :loading="tableLoading"
                     @click="refreshForwardTable(index)">清空转发表
          </el-button>
          <span class="title-info">{{ table.name }}</span>
          <el-table border stripe v-loading="tableLoading"
                    :data="table.data">
            <el-table-column
                prop="name"
                label="节点名/地址">
            </el-table-column>
            <el-table-column
                prop="port"
                label="端口">
            </el-table-column>
            <el-table-column
                prop="updateTime"
                label="更新时间">
            </el-table-column>
          </el-table>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import * as echarts from 'echarts';
import {bridgeImg, hubImg, nodeImg} from '@/assets/base64Img.js'
import TimeCountDown from "./TimeCountDown"
import {getCurrentTime, generateRandomColor} from '../util/commonUtils'

const demoData = {
  "name": "Bridge-1",
  "type": "bridge",
  "number": "",
  "children": [
    // Bridge-2
    {
      "name": "Bridge-2",
      "type": "bridge",
      "number": "",
      "children": [
        {
          "ifend": "1",
          "type": "node",
          "name": "node-1",
        },
        {
          "ifend": "1",
          "type": "node",
          "name": "node-2",
        },
        {
          "ifend": "1",
          "type": "node",
          "name": "node-3",
        }
      ],
    },
    // 直连节点
    {
      "ifend": "1",
      "type": "node",
      "name": "node-4",
      "number": ""
    },
    {
      "ifend": "1",
      "type": "node",
      "name": "node-5",
      "number": ""
    },
    {
      "ifend": "1",
      "type": "node",
      "name": "node-10",
      "number": ""
    },
    {
      "ifend": "1",
      "type": "node",
      "name": "node-11",
      "number": ""
    },
    // Hub 节点
    {
      "name": "Hub-1",
      "type": "hub",
      "number": "",
      "children": [
        {
          "type": "node",
          "code": "",
          "ifend": "1",
          "name": "node-6",
          "number": ""
        },
        {
          "type": "node",
          "code": "",
          "ifend": "1",
          "name": "node-7",
          "number": ""
        },
        {
          "type": "node",
          "code": "",
          "ifend": "1",
          "name": "node-8",
          "number": ""
        },
        {
          "type": "node",
          "code": "",
          "ifend": "1",
          "name": "node-9",
          "number": ""
        },
      ],
    },
    // // Hub 节点
    // {
    //   "name": "Hub-2",
    //   "type": "hub",
    //   "number": "",
    //   "children": [
    //     {
    //       "type": "node",
    //       "code": "2",
    //       "ifend": "1",
    //       "name": "node-10",
    //       "number": ""
    //     },
    //     {
    //       "type": "node",
    //       "ifend": "1",
    //       "name": "node-11",
    //       "number": ""
    //     },
    //     {
    //       "type": "node",
    //       "code": "2",
    //       "ifend": "1",
    //       "name": "node-12",
    //       "number": ""
    //     },
    //     {
    //       "type": "node",
    //       "code": "2",
    //       "ifend": "1",
    //       "name": "node-13",
    //       "number": ""
    //     },
    //   ],
    // },

  ]
};

export default {
  name: "topology",
  components: {
    TimeCountDown
  },

  data() {
    return {
      treeData: [{
        "name": "Bridge-1",
        "type": "bridge",
        "level": 1,
        "children": [
          {
            "name": "Bridge-2",
            "type": "bridge",
            "level": 2,
            "children": [
              {
                "name": "node-1",
                "type": "node",
                "level": 3,
              },
              {
                "name": "node-2",
                "type": "node",
                "level": 3,
              }
            ]
          },
          {
            "name": "node-3",
            "type": "node",
            "level": 2,
          }
        ]
      }],
      treeNodeCount: 3,
      treeBridgeCount: 2,
      treeHubCount: 0,
      dialogVisible: false,
      toolForm: {
        showSendText: true,
        autoSend: true,
        handSend: false,
        handWaiting: false,
      },
      // 传输时间 单位 s
      transmissionTime: 2,
      periodTime: 2,
      // 一个时间周期类随机发送帧的个数
      randomCount: 1,
      // echars对象
      topologyChart: {},
      // 拓扑图数据
      graphInfo: {},
      // 流动线数据
      lines: [],
      // 存储全局每个节点的状态信息
      globalStatus: new Map(),
      // 定时器
      timer: null,
      bridgeCount: 0,
      hubCount: 0,
      nodeCount: 0,
      forwardTables: [],
      tableLoading: false,
      msgSendCount: 0,
      msgReceiveCount: 0,
    }
  },

  mounted() {
    this.initEcharts(demoData);
    this.startTimer();
  },

  computed: {
    windowHeight: function () {
      return document.documentElement.clientHeight;
    },
    windowWidth: function () {
      return document.documentElement.clientWidth;
    },
    successRate: function () {
      if (this.msgSendCount === 0) {
        return "NaN"
      } else {
        let str = Number(this.msgReceiveCount / this.msgSendCount * 100).toFixed(2);
        return str + "%"
      }
    }
  },

  beforeDestroy() {
    clearInterval(this.timer);
    this.timer = null;
  },

  methods: {

    /**
     * 定时器任务
     */
    startTimer() {
      this.timer = setInterval(this.autoGenerateLines, this.periodTime * 1000);
    },

    /**
     * 使用指定数据，初始化echarts实例
     */
    initEcharts(initialData) {
      // 基于准备好的dom，初始化echarts实例
      this.topologyChart = echarts.init(document.getElementById('topologyChart'));
      // 初始化每个节点坐标信息
      let initData = this.initCartesian2dInfo(initialData);
      // 初始拓扑图信息
      this.graphInfo = this.initTopologyInfo(initData, true)
      // console.log("graphInfo", this.graphInfo)
      // 构建初始 Option
      let option = {
        backgroundColor: "#f5f5f5",
        // grid: {
        //   left: '5%',
        //   right: '5%'
        // },
        xAxis: {
          show: false,
          type: "value",
          max: this.graphInfo.len[0],
          min: this.graphInfo.len[1]
        },
        yAxis: {
          show: false,
          type: "value",
          max: this.graphInfo.leny[0],
          min: this.graphInfo.leny[1] - 10
        },
        tooltip: {},
        series: [
          {
            name: "topology",
            type: "graph",
            zlevel: 5,
            draggable: false,
            coordinateSystem: "cartesian2d", //使用二维的直角坐标系（也称笛卡尔坐标系）
            label: {
              show: true,
              color: "#000",
              position: 'bottom',
            },
            data: this.graphInfo.data,
            links: this.graphInfo.links,
            lineStyle: {
              opacity: 1,
              width: 2
            }
          },
          {
            type: "lines",
            name: "macFrame",
            coordinateSystem: "cartesian2d",
            zlevel: 1,
            animation: false,
            effect: {
              show: true,
              period: this.transmissionTime,
              trailLength: 0.01,
              symbolSize: 12,
              symbol: "circle",
              loop: true
            },
            data: this.lines,
            lineStyle: {
              width: 0
            }
          },
        ]
      };
      // 绘制拓扑结构图
      this.topologyChart.setOption(option);
    },

    /**
     * 数据预处理第一步，方式一：构建坐标系大小、节点坐标信息
     */
    initCartesian2dInfo(data) {
      data.x = 0;
      data.y = 0;
      if (!Object.prototype.hasOwnProperty.call(data, "children")) {
        data.len = [1, -1];
        data.leny = [1, 9];
        return data;
      }
      var l2 = data.children;
      var hi = parseInt(l2.length / 2);
      var xi = l2.length - hi;
      var his = this.getSum(l2, 0, hi);
      var xis = this.getSum(l2, xi - 1, l2.length);
      l2 = this.sortData(l2, hi, xi);
      data.children = l2;
      var jhi = (hi > 9 || his === 0) ? 300 : 200;
      var jxi = (xi > 9 || xis === 0) ? 300 : 200;
      var maxmin = [-100000, 100000];
      var maxminy = [-100000, 100000];
      for (var i = 0; i < l2.length; i++) {
        if (i < hi) {
          l2[i].x = this.getLocation(hi, data.x, i + 1);
          l2[i].y = data.y + jhi;
          l2[i].fx = this.getFX(hi, i, false);
        } else {
          l2[i].x = this.getLocation(xi, data.x, i - hi + 1);
          l2[i].y = data.y - jxi;
          l2[i].fx = this.getFX(xi, i - hi, true);
        }
        this.getMaxMin(maxmin, l2[i].x);
        this.getMaxMin(maxminy, l2[i].y);
        if (!Object.prototype.hasOwnProperty.call(l2[i], "children")) {
          continue;
        }
        var l3 = l2[i].children;
        for (var j = 0; j < l3.length; j++) {
          if (i < hi) {
            l3[j].x = this.getLocation(l3.length, l2[i].x, j + 1);
            l3[j].y = l2[i].y + 200;
            l3[j].fx = this.getFX(l3.length, j, false);
          } else {
            l3[j].x = this.getLocation(l3.length, l2[i].x, j + 1);
            l3[j].y = l2[i].y - 200;
            l3[j].fx = this.getFX(l3.length, j, true);
          }
          this.getMaxMin(maxmin, l3[j].x);
          this.getMaxMin(maxminy, l3[j].y);
        }
      }
      var c;
      if (hi % 2 === 0) {
        c = hi / 2;
        if (l2[c].hasOwnProperty("children")) {
          if (l2[c - 1].hasOwnProperty("children") &&
              l2[c].children[0].x < l2[c].x - 50) {
            var tem = 50 - l2[c].children[0].x;
            for (var j = 0; j < l2[c].children.length; j++) {
              l2[c].children[j].x = l2[c].children[j].x + tem;
              this.getMaxMin(maxmin, l2[c].children[j].x);
            }
            l2[c].x = this.getLocation2(l2[c].children);
          }
        }
      } else {
        c = parseInt(hi / 2);
      }
      for (var i = c + 1; i < hi; i++) {
        if (l2[i].hasOwnProperty("children")) {
          // eslint-disable-next-line no-prototype-builtins
          if (l2[i - 1].hasOwnProperty("children") &&
              l2[i - 1].children[l2[i - 1].children.length - 1].x - l2[i].children[0].x > -100) {
            var tem = l2[i - 1].children[l2[i - 1].children.length - 1].x + 100 - l2[i].children[0].x;
            for (var j = 0; j < l2[i].children.length; j++) {
              l2[i].children[j].x = l2[i].children[j].x + tem;
              this.getMaxMin(maxmin, l2[i].children[j].x);
            }
            l2[i].x = this.getLocation2(l2[i].children);
          }
        }
      }
      for (var i = c - 1; i >= 0; i--) {
        if (l2[i].hasOwnProperty("children")) {
          if (l2[i + 1].hasOwnProperty("children") &&
              l2[i + 1].children[0].x - l2[i].children[l2[i].children.length - 1].x < 100) {
            var tem = l2[i].children[l2[i].children.length - 1].x - (l2[i + 1].children[0].x - 100);
            for (var j = 0; j < l2[i].children.length; j++) {
              l2[i].children[j].x = l2[i].children[j].x - tem;
              this.getMaxMin(maxmin, l2[i].children[j].x);
            }
            l2[i].x = this.getLocation2(l2[i].children);
          }
        }
      }

      if (xi % 2 === 0) {
        c = xi / 2 + hi;
        if (l2[c].hasOwnProperty("children")) {
          if (l2[c - 1].hasOwnProperty("children") &&
              l2[c].children[0].x < l2[c].x - 50) {
            var tem = 50 - l2[c].children[0].x;
            for (var j = 0; j < l2[c].children.length; j++) {
              l2[c].children[j].x = l2[c].children[j].x + tem;
              this.getMaxMin(maxmin, l2[c].children[j].x);
            }
            l2[c].x = this.getLocation2(l2[c].children);
          }
        }
      } else {
        c = parseInt(xi / 2) + hi;
      }
      for (var i = c + 1; i < l2.length; i++) {
        if (l2[i].hasOwnProperty("children")) {
          if (l2[i - 1].hasOwnProperty("children") &&
              l2[i - 1].children[l2[i - 1].children.length - 1].x - l2[i].children[0].x > -100) {
            var tem = l2[i - 1].children[l2[i - 1].children.length - 1].x + 100 - l2[i].children[0].x;
            for (var j = 0; j < l2[i].children.length; j++) {
              l2[i].children[j].x = l2[i].children[j].x + tem;
              this.getMaxMin(maxmin, l2[i].children[j].x);
            }
            l2[i].x = this.getLocation2(l2[i].children);
          }
        }
      }
      for (var i = c - 1; i >= hi; i--) {
        if (l2[i].hasOwnProperty("children")) {
          if (l2[i + 1].hasOwnProperty("children") &&
              l2[i + 1].children[0].x - l2[i].children[l2[i].children.length - 1].x < 100) {
            var tem = l2[i].children[l2[i].children.length - 1].x - (l2[i + 1].children[0].x - 100);
            for (var j = 0; j < l2[i].children.length; j++) {
              l2[i].children[j].x = l2[i].children[j].x - tem;
              this.getMaxMin(maxmin, l2[i].children[j].x);
            }
            l2[i].x = this.getLocation2(l2[i].children);
          }
        }
      }
      // data.x = (maxmin[0] - maxmin[1]) / 2 + maxmin[1];
      if (maxmin[0] - maxmin[1] <= 400) {
        maxmin[0] = maxmin[0] + 100;
        maxmin[1] = maxmin[1] - 100;
      }
      data.len = maxmin;
      data.leny = maxminy;
      return data;
    },

    /**
     * 初始化拓扑图信息: links labels
     */
    initTopologyInfo(returninfo, updateGlobalStatus=true) {
      let res = {};
      let nodes = [];
      let links = [];
      this.generateGraphLinksAndNodes(returninfo, returninfo, nodes, links, updateGlobalStatus);
      console.log("globalStatus", this.globalStatus)
      res.data = nodes;
      res.links = links;
      res.len = returninfo.len;
      res.leny = returninfo.leny;
      return res;
    },

    /**
     * 显示自定义拓扑结构
     */
    showSelfDefineTopology() {
      // 基于准备好的dom，初始化echarts实例
      let selfTopologyChart = echarts.init(document.getElementById('selfTopologyChart'));
      // 初始化每个节点坐标信息
      let treeData = this.treeData[0];
      let initData = this.initCartesian2dInfo(treeData);
      // 初始拓扑图信息
      let selfGraphInfo = this.initTopologyInfo(initData, false)
      // 构建初始 Option
      let option = {
        backgroundColor: "#f5f5f5",
        // grid: {
        //   left: '5%',
        //   right: '5%'
        // },
        xAxis: {
          show: false,
          type: "value",
          max: selfGraphInfo.len[0],
          min: selfGraphInfo.len[1]
        },
        yAxis: {
          show: false,
          type: "value",
          max: selfGraphInfo.leny[0],
          min: selfGraphInfo.leny[1] - 10
        },
        tooltip: {},
        series: [
          {
            name: "topology",
            type: "graph",
            zlevel: 5,
            draggable: false,
            coordinateSystem: "cartesian2d", //使用二维的直角坐标系（也称笛卡尔坐标系）
            label: {
              show: true,
              color: "#000",
              position: 'bottom',
            },
            data: selfGraphInfo.data,
            links: selfGraphInfo.links,
            lineStyle: {
              opacity: 1,
              width: 2
            }
          },
        ]
      };
      // 绘制拓扑结构图
      selfTopologyChart.setOption(option);
    },

    /**
     * 自定义拓扑结构确认
     */
    handleSelfDefineConfirm() {
      this.dialogVisible = false;
      // 数据清空
      this.lines = []
      this.globalStatus = new Map()
      this.bridgeCount = 0
      this.hubCount = 0
      this.nodeCount = 0
      this.msgSendCount = 0
      this.msgReceiveCount = 0
      this.forwardTables = []
      // 重新渲染
      let treeData = this.treeData[0];
      this.initEcharts(treeData)
    },

    /**
     * 手动生成流动状态
     */
    handGenerateLines() {
      this.toolForm.handWaiting = true;
      this.generateLines();
      this.toolForm.handWaiting = false;
    },

    /**
     * 自动生成流动状态
     */
    autoGenerateLines() {
      // 自动流程
      if (this.toolForm.autoSend) {
        this.generateLines();
      }
    },

    /**
     * 清空转发表
     */
    refreshForwardTable(index) {
      if (this.forwardTables[index]) {
        this.tableLoading = true;
        let bridgeNodeName = this.forwardTables[index].name
        this.globalStatus.get(bridgeNodeName).forwardTable.clear();
        this.forwardTables[index].data = []
        this.tableLoading = false;
      }
    },

    /**
     * 根据上一轮的流动状态生成本轮流动状态
     *  算法实现核心：
     */
    generateLines() {
      // lines 重新渲染，初始清空，防止闪烁
      this.topologyChart.setOption({
        series: [
          {
            // 根据名字对应到相应的系列
            name: 'macFrame',
            data: [],
          }
        ]
      })
      let newLines = [];
      // 处理旧lines
      // 路由算法核心实现
      for (let i = 0; i < this.lines.length; i++) {
        let oldLine = this.lines[i];
        // console.log("oldLine", oldLine)
        let startNode = this.globalStatus.get(oldLine.endNodeName);
        let endNode = {}
        // 网桥转发
        if (startNode.type === 'bridge') {
          // 1. 自适应算法
          let forwardInfo = startNode.forwardTable.get(oldLine.sourceNodeName);
          // 1.1 转发表不存在记录，添加记录
          if (forwardInfo == null || forwardInfo === {}) {
            let port = "--";
            for (let j = 0; j < startNode.accessPoint.length; j++) {
              if (startNode.accessPoint[j].name === oldLine.startNodeName) {
                port = startNode.accessPoint[j].port;
                break;
              }
            }
            // 新增转发记录
            startNode.forwardTable.set(oldLine.sourceNodeName, {
              port: port,
              nextName: oldLine.startNodeName,
              updateTime: getCurrentTime()
            })
          }
          // 1.2 转发表存在记录，更新转发表
          else {
            // TODO 拓扑结构变动时，需更新端口等信息，以使转发表记录最新状态
            startNode.forwardTable.set(oldLine.sourceNodeName, {
              port: forwardInfo.port,
              nextName: forwardInfo.nextName,
              updateTime: getCurrentTime()
            })
          }
          // 2. 转发帧
          let nextInfo = startNode.forwardTable.get(oldLine.targetNodeName);
          // 2.1 目的地记录不存在，转发到可达节点，本次端口除外
          if (nextInfo == null || nextInfo === {}) {
            for (let j = 0; j < startNode.accessPoint.length; j++) {
              if (startNode.accessPoint[j].name === oldLine.startNodeName) {
                // do nothing
              } else {
                // 转发到可达节点
                let line = this.generateLineInfo(startNode, startNode.accessPoint[j], oldLine.sourceNodeName, oldLine.targetNodeName, oldLine.lineStyle.color);
                newLines.push(line)
              }
            }
          }
          // 2.2 目的地记录存在，转发到一下节点
          else {
            endNode = this.globalStatus.get(nextInfo.nextName);
            let line = this.generateLineInfo(startNode, endNode, oldLine.sourceNodeName, oldLine.targetNodeName, oldLine.lineStyle.color);
            newLines.push(line)
          }
        }
        // Hub 集线器转发
        else if (startNode.type === 'hub') {
          for (let j = 0; j < startNode.accessPoint.length; j++) {
            if (startNode.accessPoint[j].name !== oldLine.startNodeName) {
              let line = this.generateLineInfo(startNode, startNode.accessPoint[j], oldLine.sourceNodeName, oldLine.targetNodeName, oldLine.lineStyle.color);
              newLines.push(line);
            }
          }
        }
        // node
        else if (startNode.type === 'node') {
          if (startNode.name === oldLine.targetNodeName) {
            this.msgReceiveCount++;
            if (this.toolForm.showSendText) {
              let msg = "[" + startNode.name + "] received from [" + oldLine.sourceNodeName + "]"
              this.$notify({
                title: '接收成功',
                message: msg,
                position: 'bottom-left',
                type: 'success',
                duration: 2000
              });
            }
            console.log("congratulations! the [" + startNode.name + "] has received the MAC Frame from the [" + oldLine.sourceNodeName + "] successfully.")
          }
        }
      }
      // 随机生成MAC帧 发送-接受节点
      for (let i = 0; i < this.randomCount; i++) {
        let nodes = this.generateRandomNodes();
        let sourceNode = nodes[0];
        let startNode = nodes[0];
        let endNode = this.globalStatus.get(startNode.name).accessPoint[0];
        let targetNode = nodes[1];
        console.log("mac frame send from: " + sourceNode.name + ", to: " + targetNode.name);
        let line = this.generateLineInfo(startNode, endNode, sourceNode.name, targetNode.name, "", true);
        newLines.push(line)
        this.msgSendCount++;
      }
      this.lines = newLines;
      // lines 重新渲染，再次渲染，继续后续状态
      this.topologyChart.setOption({
        series: [
          {
            // 根据名字对应到相应的系列
            name: 'macFrame',
            data: this.lines,
          }
        ]
      })
      // 网桥转发表
      // this.forwardTables = []
      let forwardTables = []
      this.globalStatus.forEach(entry => {
        if (entry.type === 'bridge') {
          let forwardItem = {
            name: entry.name,
            data: []
          };
          entry.forwardTable.forEach((value, key) => {
            let tt = {
              name: key,
              port: value.port,
              nextName: value.port,
              updateTime: value.updateTime
            }
            forwardItem.data.push(tt)
          })
          forwardItem.data.sort((a, b) => {
            let x = a.updateTime
            let y = b.updateTime
            return ((x < y) ? 1 : (x > y) ? -1 : 0)
          })
          forwardTables.push(forwardItem)
        }
      })
      this.forwardTables = forwardTables
    },

    handleHandSendChange(val) {
      if (val) {
        this.toolForm.autoSend = false;
      }
      this.lines = []
      // lines 重新渲染，清空历史数据
      this.topologyChart.setOption({
        series: [
          {
            // 根据名字对应到相应的系列
            name: 'macFrame',
            data: this.lines,
          }
        ]
      })
    },

    handleAutoSendChange(val) {
      if (!val) {
        this.lines = []
        // lines 重新渲染，清空历史数据
        this.topologyChart.setOption({
          series: [
            {
              // 根据名字对应到相应的系列
              name: 'macFrame',
              data: this.lines,
            }
          ]
        })
      } else {
        this.toolForm.handSend = false;
      }
    },

    handleSelfDefine() {
      this.dialogVisible = true;
    },

    /**
     * 删除树形节点
     */
    treeRemove(node, data) {
      const parent = node.parent;
      const children = parent.data.children || parent.data;
      const index = children.findIndex(d => d.name === data.name);
      children.splice(index, 1);
      if (data.type === 'bridge') {
        this.treeBridgeCount--;
      } else if (data.type === 'hub') {
        this.treeHubCount--;
      } else if (data.type === 'node') {
        this.treeNodeCount--;
      }
    },

    /**
     * 新增树形节点
     */
    handleAddCommand(content) {
      console.log("handleAddCommand", content)
      let name = "";
      let level = content.node.data.level + 1;
      if (content.type === 'bridge') {
        name = "Bridge-" + (++this.treeBridgeCount);
      } else if (content.type === 'hub') {
        name = "Hub-" + (++this.treeHubCount);
      } else if (content.type === 'node') {
        name = "node-" + (++this.treeNodeCount);
      }
      let newChild = {};
      if (content.type === 'node' || level >= 3) {
        newChild = {name: name, type: content.type, ifend: "1", level: level}
      } else {
        newChild = {name: name, type: content.type, level: level}
      }
      if (!content.node.data.children) {
        this.$set(content.node.data, 'children', []);
      }
      content.node.data.children.push(newChild);
    },

    /**
     * 生成流动线信息
     */
    generateLineInfo(startNode, endNode, sourceNodeName, targetNodeName, color = "", isSourceNode = false) {
      // let color = (startNode.code === '2' || startNode.code === 2) ? 'rgba(199,80,80,0.5)' : 'rgba(55,155,255,0.5)';
      if (color == null || color === '') {
        color = generateRandomColor();
      }
      let coords = [];
      // 本次流动的line 对应的开始和结束节点
      coords.push(startNode.coords)
      coords.push(endNode.coords)
      // 发送节点信息
      let labelFormatter = ""
      if (isSourceNode) {
        labelFormatter = "To " + targetNodeName
      }
      return {
        coords: coords,
        startNodeName: startNode.name,
        endNodeName: endNode.name,
        sourceNodeName: sourceNodeName,
        targetNodeName: targetNodeName,
        lineStyle: {
          curveness: 0,
          color: color
        },
        label: {
          show: true,
          formatter: labelFormatter,
          position: "middle",
          color: "#E6A23C",
          fontSize: 14,
          // fontWeight: "bold"
        }
      }
    },

    /**
     * 随机生成开始和结束节点
     */
    generateRandomNodes() {
      let startNode = this.randomNum(1, this.nodeCount);
      let endNode = this.randomNum(1, this.nodeCount);
      while (startNode === endNode) {
        endNode = this.randomNum(1, this.nodeCount);
      }
      let startNodeName = "node-" + startNode;
      let endNodeName = "node-" + endNode;
      let startNodeInfo = this.globalStatus.get(startNodeName);
      let endNodeInfo = this.globalStatus.get(endNodeName);
      return [startNodeInfo, endNodeInfo];
    },

    /**
     * 递归生成拓扑结构的连线信息、端口信息、数据节点
     */
    generateGraphLinksAndNodes(parentNode, initData, nodes, links, updateGlobalStatus=true) {
      if (initData === undefined || initData == null) {
        return
      }
      // 节点数据统计
      this.computeNodeCount(initData.type);
      // 节点信息
      let node = {
        name: initData.name,
        tooltip: {
          formatter: "{b}"
        },
        label: {
          show: true,
          position: 'bottom',
          distance: 8,
          formatter: initData.name,
          fontSize: 16,
          fontWeight: 'bold'
        },
        symbol: this.selectIcon(initData.type),
        symbolSize: [64, 64],
        value: [initData.x, initData.y],
        ifend: initData.ifend,
        fx: initData.fx
      }
      // 添加节点
      nodes.push(node);
      // 可达节点
      let accessPoint = []
      if ("children" in initData) {
        // 递归遍历子节点
        let children = initData.children;
        for (let i = 0; i < children.length; i++) {
          // 构造连线信息
          let link = this.generateGraphLinkInfo(initData, children[i], i + 1);
          // 添加连线
          links.push(link);
          // 构造可达点信息
          let pointInfo = {
            name: children[i].name,
            fx: children[i].fx,
            type: children[i].type,
            coords: [children[i].x, children[i].y],
            port: link.port
          }
          // 添加可达点
          accessPoint.push(pointInfo);
          // 递归遍历
          this.generateGraphLinksAndNodes(initData, children[i], nodes, links);
        }
        // bridge子节点到bridge父节点特殊连线
        if (parentNode != null && parentNode !== initData && parentNode.type === 'bridge' && initData.type === 'bridge') {
          // 构造连线信息
          let link = this.generateGraphLinkInfo(initData, parentNode, children.length + 1);
          // 添加连线
          links.push(link);
          // 构造可达点信息
          let pointInfo = {
            name: parentNode.name,
            type: parentNode.type,
            fx: parentNode.fx,
            coords: [parentNode.x, parentNode.y],
            port: link.port
          }
          // 添加可达点
          accessPoint.push(pointInfo);
        }
        // hub节点到bridge节点可达性
        if (parentNode != null && parentNode.type === 'bridge' && initData.type === 'hub') {
          // 构造可达点信息
          let pointInfo = {
            name: parentNode.name,
            type: parentNode.type,
            fx: parentNode.fx,
            coords: [parentNode.x, parentNode.y],
            port: "--"
          }
          // 添加可达点
          accessPoint.push(pointInfo);
        }
      } else {
        // 构造可达点信息
        let pointInfo = {
          name: parentNode.name,
          type: parentNode.type,
          fx: parentNode.fx,
          coords: [parentNode.x, parentNode.y],
          curveness: initData.fx === 1 ? 0.1 : -0.1,
          code: initData.code,
          port: "--"
        }
        // 添加可达点
        accessPoint.push(pointInfo);
      }
      // 新增全局状态信息
      if (initData.type === 'bridge') {
        if (updateGlobalStatus) {
          // 网桥类型添加转发表
          this.globalStatus.set(initData.name, {
            name: initData.name,
            fx: initData.fx,
            type: initData.type,
            coords: [initData.x, initData.y],
            accessPoint: accessPoint,
            forwardTable: new Map()
          })
        }
      } else {
        if (updateGlobalStatus) {
          this.globalStatus.set(initData.name, {
            name: initData.name,
            fx: initData.fx,
            type: initData.type,
            coords: [initData.x, initData.y],
            curveness: initData.fx === 1 ? 0.1 : -0.1,
            code: initData.code,
            accessPoint: accessPoint
          })
        }
      }
    },

    /**
     * 生成拓扑图节点连线信息
     */
    generateGraphLinkInfo(sourceNode, targetNode, number) {
      // 连接线对称
      // let curveness = targetNode.fx === 1 ? 0.1 : -0.1;
      let curveness = 0;
      let colork = (targetNode.code === '2' || targetNode.code === 2) ? '#C75050' : '#53B5EA';
      // port label && padding
      let padding = [0, 0, 0, 0];
      let port = "";
      if (sourceNode.type === 'bridge') {
        // 生成 label padding
        let vector = [targetNode.x - sourceNode.x, targetNode.y - sourceNode.y];
        padding = this.generatePadding(vector)
        port = "P-" + number;
      }
      // 连线信息
      return {
        source: sourceNode.name,
        target: targetNode.name,
        port: port,
        lineStyle: {
          curveness: curveness,
          color: colork
        },
        label: {
          show: true,
          position: 'middle',
          padding: padding,
          formatter: port,
          fontSize: 14,
          fontWeight: 'bold',
          color: '#f759ab'
        }
      };
    },

    // 生成label偏移信息
    generatePadding(vector) {
      let padding = [0, 0, 0, 0]
      if (vector[0] === 0 && vector[1] < 0) {
        // 4 下移 padding top
        let yDis = Math.abs(vector[1] * 0.4)
        padding = [0, yDis, 0, 0];
        // console.log(i , vector, " padding = ", padding)
      } else if (vector[0] === 0 && vector[1] > 0) {
        // 上移 padding
        let yDis = Math.abs(vector[1] * 0.4)
        padding = [0, yDis, -20, 0];
        // console.log(i , vector, " padding = ", padding)
      } else if (vector[0] > 0) {
        let tan = vector[1] / vector[0];
        let xDis = Math.abs(vector[0])
        let yDis = Math.abs(vector[1] * 0.05)
        if (vector[1] > 0) {
          padding = [0, 0, -yDis, -xDis];
          // console.log( i, vector, tan, padding)
        } else {
          padding = [0, 0, -yDis, -xDis];
          // console.log( i, vector, tan, padding)
        }
      } else if (vector[0] < 0) {
        let tan = vector[1] / vector[0];
        let xDis = Math.abs(vector[0])
        let yDis = Math.abs(vector[1] * 0.05)
        if (vector[1] > 0) {
          padding = [0, 0, -yDis, xDis];
          // console.log( i, vector, tan, padding)
        } else {
          padding = [0, 0, -yDis, xDis];
          // console.log( i, vector, tan, padding)
        }
      }
      return padding;
    },

    // 根据类型选择icon
    selectIcon(nodeType) {
      if (nodeType === 'bridge') {
        return bridgeImg;
      } else if (nodeType === 'hub') {
        return hubImg;
      }
      return nodeImg;
    },

    /**
     * 以下为工具方法
     */

    /**
     * 数据预处理第一步，方式二：
     * 构建坐标系大小、节点坐标信息
     */
    format2(data) {
      if (!data.hasOwnProperty("children")) {
        return data;
      }
      var l2 = data.children;
      var hi = parseInt(l2.length / 2);
      var xi = l2.length - hi;
      var maxmin = data.len;
      for (var i = 0; i < l2.length; i++) {
        if (!l2[i].hasOwnProperty("children")) {
          if (i > 0 && (i != hi) && l2[i - 1].hasOwnProperty("children") && l2[i - 1].children.length > 5) {
            var tem = (l2[i - 1].children.length - 5) / 2;
            l2[i].x = l2[i].x + 100 * tem;
            var len, flag = false;
            if (i < hi) {
              len = hi;
            } else {
              len = l2.length;
            }
            for (var j = i + 1; j < len; j++) {
              flag = true;
              l2[j].x = l2[j].x + 100 * tem;
              if (l2[j].hasOwnProperty("children")) {
                var children = l2[j].children;
                for (var k = 0; k < children.length; k++) {
                  children[k].x = children[k].x + 100 * tem;
                  this.getMaxMin(maxmin, children[k].x);
                }
              }
              this.getMaxMin(maxmin, l2[j].x);
            }
            if (flag) {
              l2[i].x = l2[i].x > maxmin[0] ? maxmin[0] : l2[i].x;
            }
          } else if (i < l2.length - 1 && l2[i + 1].hasOwnProperty("children") && l2[i + 1].children.length > 5) {
            var tem = (l2[i + 1].children.length - 5) / 2;
            l2[i].x = l2[i].x - 100 * tem;
            var len, flag = false;
            if (i < hi) {
              len = 0;
            } else {
              len = hi;
            }
            for (var j = i - 1; j >= len; j--) {
              flag = true;
              l2[j].x = l2[j].x - 100 * tem;
              if (l2[j].hasOwnProperty("children")) {
                var children = l2[j].children;
                for (var k = 0; k < children.length; k++) {
                  children[k].x = children[k].x - 100 * tem;
                  this.getMaxMin(maxmin, children[k].x);
                }
              }
              this.getMaxMin(maxmin, l2[j].x);
            }
            if (flag) {
              l2[i].x = l2[i].x < maxmin[1] ? maxmin[1] : l2[i].x;
            }
          }
        }
      }
      // data.x = (maxmin[0] - maxmin[1]) / 2 + maxmin[1];
      if (maxmin[0] - maxmin[1] <= 400) {
        maxmin[0] = maxmin[0] + 100;
        maxmin[1] = maxmin[1] - 100;
      }
      data.len = maxmin;
      return data;
    },

    getLocation(num, x, index) {
      if (num % 2 === 0) {
        let c = num / 2 - index;
        x = x - 50 - 100 * c;
      } else {
        let c = parseInt(num / 2 + 1) - index;
        x = x - 100 * c;
      }
      return x;
    },

    getLocation2(data) {
      var len = data.length;
      var x;
      if (len % 2 === 0) {
        let c = len / 2;
        x = data[c - 1].x + 50;
      } else {
        let c = parseInt(len / 2 + 1);
        x = data[c - 1].x;
      }
      return x;
    },

    getFX(len, index, fx) {
      var x, c;
      if (len < 1) {
        return 1;
      }
      c = (len % 2 === 0) ? len / 2 : (parseInt(len / 2) + 1);
      x = (index < c) ? 1 : -1;
      if (fx) {
        x = -x;
      }
      return x;
    },

    getSum(data, st, ed) {
      if (ed > data.length) {
        ed = data.length;
      }
      var count = 0;
      for (var i = st; i < ed; i++) {
        if (!data[i].hasOwnProperty("children")) {
          continue;
        }
        var l3 = data[i].children;
        count = count + l3.length;
      }
      return count;
    },

    sortData(data, hi, xi) {
      var res = [];
      var len = hi - 1;
      var pos = 0;
      for (var i = 0; i < hi - 1; i++) {
        var flag = 1;
        for (var j = 0; j < len; j++) {
          var dx = !data[j].hasOwnProperty("children") ? 0 : data[j].children.length,
              dx1 = !data[j + 1].hasOwnProperty("children") ? 0 : data[j + 1].children.length;
          if (dx > dx1) {
            var tem = data[j];
            data[j] = data[j + 1];
            data[j + 1] = tem;
            flag = 0;
            pos = j;
          }
        }
        len = pos;
        if (flag === 1) {
          break;
        }
      }
      for (var i = 0; i < hi; i++) {
        if (i % 2 === 0) {
          res[i / 2] = data[i];
        } else {
          res[hi - (i + 1) / 2] = data[i];
        }
      }

      len = data.length - 1;
      pos = 0;
      for (var i = hi; i < data.length - 1; i++) {
        var flag = 1;
        for (var j = hi; j < len; j++) {
          var dx = !data[j].hasOwnProperty("children") ? 0 : data[j].children.length,
              dx1 = !data[j + 1].hasOwnProperty("children") ? 0 : data[j + 1].children.length;
          if (dx > dx1) {
            var tem = data[j];
            data[j] = data[j + 1];
            data[j + 1] = tem;
            flag = 0;
            pos = j;
          }
        }
        len = pos;
        if (flag === 1) {
          break;
        }
      }
      for (var i = 0; i < xi; i++) {
        if (i % 2 === 0) {
          res[i / 2 + hi] = data[i + hi];
        } else {
          res[xi - (i + 1) / 2 + hi] = data[i + hi];
        }
      }
      return res;
    },

    getMaxMin(maxmin, x) {
      if (maxmin[0] < x) {
        maxmin[0] = x;
      }
      if (maxmin[1] > x) {
        maxmin[1] = x;
      }
      return maxmin;
    },

    computeNodeCount(type) {
      if (type === 'bridge') {
        this.bridgeCount = this.bridgeCount + 1
      } else if (type === 'hub') {
        this.hubCount = this.hubCount + 1
      } else if (type === 'node') {
        this.nodeCount = this.nodeCount + 1
      }
    },

    // 随机数 [n, m]
    randomNum(minNum, maxNum) {
      switch (arguments.length) {
        case 1:
          return Math.floor(Math.random() * minNum + 1);
        case 2:
          return Math.floor(Math.random() * (maxNum - minNum + 1) + minNum);
        default:
          return 0;
      }
    },

  }
}
</script>

<style scoped>
.box {
  display: flex;
  flex-flow: row nowrap;
  height: 100%;
  width: 100%;
}

.dialog-box {
  width: 80%;
  min-height: 600px;
}

.form-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
}

.info-box {
  text-align: center;
  margin-left: 20px;
  width: 30%;
  height: 100%;
}

.title-info {
  font-size: 16px;
  font-weight: bold;
  color: #409EFF;
  line-height: 1.5em;
}

.table-box {
  width: 45%;
  margin: 0 10px;
}

.p-wrapper {
  font-size: 15px;
  font-weight: bold;
  color: #409EFF;
  line-height: 1.5em;
}

/deep/ .custom-tree-node {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 16px;
  padding-right: 8px;
}
</style>